#include "lib_MonitorDiskIo.h"
#include <stdio.h>
#include <string.h>
using namespace std;

MonitorDiskIo::MonitorDiskIo(string& dev_name)
    :Disk_Name_(dev_name)
{
    sysDiskIo_[0].read_count = 0;
    sysDiskIo_[0].dev_name = Disk_Name_;
    sysDiskIo_[1].read_count = 0;
    sysDiskIo_[1].dev_name = Disk_Name_;
    update_diskIo(false);
}

MonitorDiskIo::~MonitorDiskIo()
{

}

diskIo_t MonitorDiskIo::read_diskState(const string dev_name)
{
    FILE* fp = fopen("/proc/diskstats", "r");
    if (fp == NULL) {
        perror("fopen /proc/diskstats failed");
        exit(-1);
    }

    char buf[1024];
    int found = 0;

    diskIo_t diskio;
    memset(&diskio, 0, sizeof(diskio));
    clock_gettime(CLOCK_MONOTONIC, &diskio.ts);

    unsigned int major, minor;  // 主设备号、次设备号（无关，但需占位）
    char dev[32];               // 设备名（如 "sda"）
    // 逐行解析/proc/diskstats
    while (fgets(buf, sizeof(buf), fp) != NULL) {
        // 解析关键字段：major minor dev ... read_sectors ... write_sectors ...
        // 字段对应：%u %u %s ... %llu ... %llu ...（中间跳过5个无关字段）
        int ret = sscanf(
            buf,
            "%u %u %s %*u %*u %llu %*u %*u %*u %llu",
            &major, &minor, dev, &diskio.read_sectors, &diskio.write_sectors
        );

        // 匹配目标设备名
        if (ret == 5 && strncmp(dev, dev_name.c_str(), sizeof(dev) - 1) == 0)
        {
            found = 1;
            break;
        }
    }
    fclose(fp);

    if (!found)
    {
        fprintf(stderr, "未找到设备 %s 的磁盘IO信息\n", dev_name.c_str());
        exit(-1);
    }

    return diskio;
}

void MonitorDiskIo::cal_diskIoSpeed(sysDiskIo_t* prev, sysDiskIo_t* curr)
{
    double dt_time = (curr->disk_io.ts.tv_sec - prev->disk_io.ts.tv_sec) + (curr->disk_io.ts.tv_nsec - prev->disk_io.ts.tv_nsec) * 1e-9;
    curr->disk_io.read_speed = (curr->disk_io.read_sectors - prev->disk_io.read_sectors) * 512.0 / dt_time / 1024 / 1024;
    curr->disk_io.write_speed = (curr->disk_io.write_sectors - prev->disk_io.write_sectors) * 512.0 / dt_time / 1024 / 1024;
}

sysDiskIo_t* MonitorDiskIo::update_diskIo(bool opt_cal_ioSpeed)
{
    sysDiskIo_t* prev, * curr;
    if (sysDiskIo_[0].read_count == sysDiskIo_[1].read_count)
    {
        curr = &sysDiskIo_[0];
        prev = &sysDiskIo_[1];
    }
    else if (sysDiskIo_[0].read_count > sysDiskIo_[1].read_count)
    {
        curr = &sysDiskIo_[1];
        prev = &sysDiskIo_[0];
    }
    else
    {
        fprintf(stderr, "逻辑错误, 请检查代码逻辑\n");
        exit(-1);
    }
    curr->disk_io = read_diskState(Disk_Name_);

    if (opt_cal_ioSpeed)
    {
        cal_diskIoSpeed(prev, curr);
    }
    curr->read_count++;

    return curr;
}

void MonitorDiskIo::show_diskIoSpeed(sysDiskIo_t* diskIo)
{
    printf("%s read_speed: %6.2f MB/s, write_speed: %6.2f MB/s\n", diskIo->dev_name.c_str(), diskIo->disk_io.read_speed, diskIo->disk_io.write_speed);
}